﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using SimpleOrmCFPC.Apis.Db;
using SimpleOrmCFPC.Core.Dao.Helpers;
using SimpleOrmCFPC.Core.Model;
using SimpleOrmCFPC.Core.Query;
using SimpleOrmCFPC.Core.Query.OrderBys;
using SimpleOrmCFPC.Core.Query.Requests;
using SimpleOrmCFPC.Core.Query.Where;
using SimpleOrmCFPC.Helpers;

namespace SimpleOrmCFPC.Core.Dao
{
    public class LinqDao<T> : IDao<T>
        where T : new()
    {
        private readonly IDb executorP;
        private RequestExecutor requestExecutor;
        private CreateEntityRequestFactory createRequestFactory;

        public LinqDao(IDb executor_P, CreateEntityRequestFactory createRequestFactory, RequestExecutor requestExecutor)
        {
            executorP = executor_P;
            this.createRequestFactory = createRequestFactory;
            this.requestExecutor = requestExecutor;
        }

        public SQLLinq<T> Query
        {
            get { return SQLLinqFactory.Create<T>(executorP); }
        }

        public T Create(T obj)
        {
            string request = createRequestFactory.ComputeRequest<T>();
            IEnumerable<object> values = createRequestFactory.ComputeValues(obj);

            object id = executorP.ExecuteNonQuery(request, values);
            return Read(id);
        }

        public T Update(T obj)
        {
            string idName = (from property in typeof (T).GetProperties()
                             where property.GetCustomAttributes(typeof (PrimaryKeyAttribute), true).Length > 0
                             select property.Name).First();
            IEnumerable<string> names = from property in typeof (T).GetProperties() select property.Name;
            List<object> values =
                (from property in typeof (T).GetProperties() select property.GetValue(obj, new object[0])).ToList();
            string _insertSql = string.Format("update {0} set {1} where {2} = ?", typeof (T).Name,
                                              string.Join(" ,", (from n in names select n + " = ?").ToArray()), idName);
            object id = obj.GetType().GetProperty(idName).GetValue(obj, new object[0]);
            values.Add(id);
            executorP.ExecuteNonQuery(_insertSql, values);
            return obj;
        }

        public void Delete(T obj)
        {
           var keyProperty = typeof(T).GetPrimaryKey();
           var id = keyProperty.GetValue(obj, new object[0]);
            executorP.ExecuteNonQuery(string.Format("delete from {0} where {1} = ?", typeof (T).Name, keyProperty),
                                      new[] {id}.ToList());
        }

        public T Read(object id)
        {
           string keyProperty = typeof(T).GetPrimaryKey().GetColumnName();
            var tableName = typeof (T).GetEntityName();
            var sqlRequest = new SQLRequest(null, null, new List<Ordering>(), new List<IWhereElement>()
                {
                    new SimpleWhereElement(tableName + "." + keyProperty + " = ?", new[] {id}.ToList())
                }, new List<string> {tableName}, new List<string>());

            return requestExecutor.Get<T>(sqlRequest).Single();
            
        }
    }
}